package com.clerkq.FP
{	
	import com.clerkq.FP.event.FlipPageEvent;
	import com.clerkq.FP.model.FlipPageModel;
	import com.content.model.LoadedAssets.SkinProxy;
	import com.library.core.IDisposable;
	import com.library.core.UIObject;
	
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.utils.Timer;
	
	public class FlipEngine extends UIObject implements IDisposable
	{
		//======================================================================
		//  Constants
		//======================================================================
		//public static const fliparea_corner:st = 1000;
		
		//======================================================================
		//  Properties
		//======================================================================
		private var _model:FlipPageModel;
		
		private var _firstPage:int;
		private var _lastPage:int;
				
    	private var _curIndex:int = 1;
    	private var _flipCount:int = 0;	// 如:-5代表有五页正在翻动中且是从左向右翻， +3 有三页正在从右向左翻
    	private var _dragingIndex:int;
    	
		private var _pages:Object = {};
    	private var _flipPages:Object = {};
		
    	private var _stage:Stage;
    	
		//======================================================================
		//						Timers
		//======================================================================
		private var _mainTimer:Timer;
		private var _autoFlip:Timer;
		
		//======================================================================
		//						Methods ---  Public
		//======================================================================
		public function FlipEngine(model:FlipPageModel, stage:Stage)
		{
			super();
			_model = model;
			_model.addEventListener(FlipPageEvent.ON_ADDPAGE, onAddpage);
			
			_stage = stage;
			// Event and Main Cycle
			_stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			_stage.addEventListener(MouseEvent.MOUSE_UP,onStageMouseUp);
			
	        _mainTimer = new Timer(20);
			_mainTimer.addEventListener( TimerEvent.TIMER,doFlipping );
			_mainTimer.start();
			
			_autoFlip = new Timer(20);
			_autoFlip.addEventListener( TimerEvent.TIMER, detectFlip );
			
			// Start dislay
			var addOn:int = _model.hasCover? 1 : 0;
			setIndexRange(0 + addOn, 0 + addOn);
		}
		
		// TODO flipToPage AutoFlip, don not need drag operation
	    public function flipToPage(index:int, angle:Number, contInterval:int):void {
	        index = index > _lastPage ? _lastPage : (index > _firstPage ? index : _firstPage);
	        index = index & -2;
	        var cp:int = _curIndex + _flipCount * 2;
	        if (index == cp) {
	            return;
	        }
	        if ( (index > cp ? 1 : -1) * _flipCount < 0 ) {
	            stopFlip(-1000, BooleanExt.FALSE, true);
	        }
	        if (contInterval != -1) {
	            _autoFlip.stop();
	        }
	        if (contInterval > 0) {
	            var params:Object = {index:index, angle:angle};
	            autoFlip(params);
	        } else {
	            angle = ( (angle == -1000 ? 5 : (angle > 45 ? 45 : (angle < -45 ? -45 : angle))) * Math.PI ) / 180;
	            var w:Number = _model.pageWidth;
	            var h:Number = _model.pageHeight;
	            var dx:Number = w * 0.2;
	            var dy:Number = Math.tan(angle) * 1.2 * h;
	            var page:Page;
	            var gPageIndex:int;
	            if (index > cp) {
	            	// 向左翻
	            	gPageIndex = _flipCount > 0 ? (_curIndex + index - cp - 2) : (index - 2);
	                gotoPage(gPageIndex, true);
	                page = startFlip(index - 1, 0, (dy >= 0 ? 0 : h), {x:-dx, y:(dy >= 0 ? dy : h + dy)});
	            } else {
	            	// 向右翻
	            	gPageIndex = _flipCount < 0 ? (_curIndex + index - cp + 2) : (index + 2);
	                gotoPage(gPageIndex, true);
	                page = startFlip(index + 2, 0, (dy >= 0 ? 0 : h), {x:dx, y:(dy >= 0 ? dy : h + dy)});
	            }
	            
	            if(page != null){
	            	_dragingIndex = page.index
	            	_autoFlip.start();	            	
	            }
	        }
	    };
	    
	    public function flipNextPage(angle:Number):void {
	        flipToPage(_curIndex + (_flipCount + 1) * 2, angle, 20);
	    };
	    public function flipPrevPage(angle:Number):void {
	        flipToPage(_curIndex + (_flipCount - 1) * 2, angle, 20);
	    };
	    
	    public function start(pageIndex:Number = 0):void{
	    	gotoPage(pageIndex, true);
	    }
	    
		//======================================================================
		//						Methods ---  Private
		//======================================================================
		private function setIndexRange(first:int, last:int):void {
            _firstPage = (first > 0) ? (int(first)) : 0;
            _lastPage = (last > 0) ? (int(last)) : 0;
	        if (_lastPage < _firstPage) {
	            _lastPage = _firstPage;
	        }
	    };
	    
		private function createPage(index:int, depth:int):Page {
			trace("create page: " + index);
	        if ((index < _firstPage) || (index > _lastPage)) {
	            return(undefined);
	        }
	        var page:Page = new Page(index, depth);
	        page.state = "static";
	        var addOn:int = _model.hasCover? 1 : 0;
	        page.contentInfo = _model.pageList[index - addOn];
	        page.position = {};
		    addChild(page);	
		    _pages[index] = page;
		    
	    	createPageContent(page);		// 最后一步再做真实内容填充
	    	drawContentMask(page);
	    	createFlipArea(page);
	    	adjustPage(page);	// Create之后都会调整位置
		    return page;
	    };
	    
	    private function removePage(page:Page):void {
	    	if(page == null)return;
	        if (_flipPages[page.index] != null) {
	        	if(this.contains(_pages[page.index])) removeChild(_pages[page.index]);
	        	Page(_flipPages[page.index]).dispose();
	            delete _flipPages[page.index];
	            _flipCount = _flipCount + page.side;
	        }
	        if(this.contains(_pages[page.index])) removeChild(_pages[page.index]);
	        Page(_pages[page.index]).dispose();
	        delete _pages[page.index];
	        page = null;
	    };
	    
	    private function adjustPage(page:Page):void {
	        var w:Number = _model.pageWidth;
	        var h:Number = _model.pageHeight;
	        var pos:Object = page.position;
	        var side:int = page.side;
	        var r:Object; // Range
	        var top:int;
	        var sin:Number;
	        var cos:Number;
	        if (page.state == "static") {
	            pos.px = 0;
	            pos.py = 0;
	            pos.rotation = 0;
	            pos.range = {angle:0, type:1, a:w, b:w};
	        } else {
	            top = (pos.y >= pos.cy) ? 1 : -1;
	            r = getMaskRange( pos.x * -side, top > 0 ? pos.y : (h - pos.y), pos.cx, top > 0 ? pos.cy : h - pos.cy );
	            sin = Math.sin(r.angle * 2);
	            cos = Math.cos(r.angle * 2);
	            var w2:Number = w - pos.cx * ((r.a + r.b) > 0 ? 1 : -1);
	            pos.px = pos.x + w2 * cos * (-side);
	            pos.py = pos.y - w2 * sin * top;
	            pos.rotation = (side < 0 ? r.angle : (Math.PI - r.angle)) * top * (-2) * 180 / Math.PI;
	            pos.range = r;
	        }
            page.page.x = pos.px;
            page.page.y = pos.py;
            page.page.rotation = pos.rotation;
	            
	        // Pos.cy 的值在拖动左上角和右上角时没有效果，都是0。但是在拖动两侧以及左下、右下角的时候有用。
	        if(pos.cy){	        	
		        page.face.y = -pos.cy;
		        page.contentMask.y = -pos.cy;
	        }else{
		        page.face.y = 0;
		        page.contentMask.y = 0;
	        }
	        
	        var mask:Sprite = page.pageMask;
	        mask.graphics.clear();
	        mask.graphics.beginFill(0x000000, 1.0);
	        if (r == null) {
	            drawRect(mask, w * side, 0, w * (-side), h);
	        } else if (r.type == 0) {
	            drawRect(mask, w * side, top > 0 ? 0 : h, (w * 2 - r.a) * (-side), r.b * top, (w * 2) * (-side));
	            if (r.a > r.b) {
	                drawRect(mask, w * side, (top > 0 ? r.b : (h - r.b)), (w * 2) * (-side), (r.b * (-cos)) * top);
	            }
	        } else {
	            drawRect(mask, w * side, (top > 0 ? 0 : h), (w * 2 - r.a) * (-side), h * top, (w * 2 - r.b) * (-side));
	            drawRect(mask, w * side, (top > 0 ? h : 0), (w * 2 - r.b) * (-side), r.b * sin * top);
	        }
	        mask.graphics.endFill();
	        if (r != null) {
	            mask = Page(_pages[page.index + side]).pageMask;
	            mask.graphics.clear();
	            mask.graphics.beginFill(0x000000, 1.0);
	            if (r.type == 0) {
	                drawRect(mask, 0, (top > 0 ? 0 : h), (w - r.a) * (-side), r.b * top, w * (-side));
	                drawRect(mask, 0, (top > 0 ? r.b : (h - r.b)), w * (-side), (h - r.b) * top);
	            } else {
	                drawRect(mask, 0, (top > 0 ? 0 : h), (w - r.a) * (-side), h * top, (w - r.b) * (-side));
	            }
	            mask.graphics.endFill();
	        }
	        if (r == null) {
	            pos.ratio = 1;
	        } else {
	            pos.ratio = (r.a * Math.cos(r.angle)) / w;
	        }
	        setShadeStyle(page);
        	setPageShadowStyle(page);
	    };
	    
	    private function createPageContent(page:Page):void {
	        var content:MovieClip = new MovieClip();
	        
	        //----------------- Draw page start
	        content.graphics.beginFill(/*Math.random()*16777216*/0xffffff, 1.0);
			content.graphics.drawRect(0, 0, _model.pageWidth, _model.pageHeight);
			
			// Load Content
			page.load();
			//----------------- Draw page end
			
	        content.index = page.index;
	        content.owner = page;
	        page.content = content;
	        page.face.addChild(content);
	        setPageFrame(page);
	        setEvenPage(page);
	    };
	    
	    private function drawContentMask(page:Page):void {
	        var mask:Sprite = page.contentMask;
	        mask.graphics.clear();
            mask.graphics.beginFill(0x000000, 0.5);
            drawRect(mask, 0, 0, (_model.pageWidth * page.side), _model.pageHeight);
            mask.graphics.endFill();
	    };
	    
	    private function drawShadowMask(page:Page):void {
	        var m:Sprite = page.shadowMask;
	        if (m == null) {
	            return;
	        }
	        m.graphics.clear();
	        var params:Object = {page:page, shadowMask:m, done:false};
	        if (!params.done) {
	            m.graphics.beginFill(0x000000, 1.0);
	            drawRect(m, _model.pageWidth * page.side, 0, (_model.pageWidth * 2) * (-page.side), _model.pageHeight);
	            m.graphics.endFill();
	        }
	    };
	    
	    private function validatePos(page:Page, aim:Boolean):void {
	        var pos:Object = page.position;
	        var xn:String = (aim ? "ax" : "x");
	        var yn:String = (aim ? "ay" : "y");
	        var x:Number = pos[xn];
	        var y:Number = pos[yn];
	        var w:Number = (_model.pageWidth - pos.cx) - (pos[xn] * page.side > 0 ? 0 : 1);
	        var h:Number = _model.pageHeight;
	        var cy:Number = pos.cy;
	        var area:Number = 0;
	        if (Math.abs(x) > w) {
	            var tan:Number = (y - cy) / (Math.abs(x) - w);
	            area = tan > (cy / w) ? 1 : (tan < (cy - h) / w ? -1 : 0);
	        } else {
	            area = (y > cy) ? 1 : -1;
	        }
	        if (area == 0) {
	            pos[xn] = w * (x > 0 ? 1 : -1);
	            pos[yn] = cy;
	        } else {
	            var y2:Number = area > 0 ? y : (h - y);
	            var cy2:Number = area > 0 ? cy : (h - cy);
	            var ratio:Number = (x * x + y2 * y2) / (w * w + cy2 * cy2);
	            if (ratio > 1) {
	                ratio = Math.sqrt(ratio);
	                y2 = y2 / ratio;
	                pos[xn] = x / ratio;
	                pos[yn] = area > 0 ? y2 : (h - y2);
	            }
	        }
	    };
	    
	    private function createFlipArea(page:Page):void {
	        var setting:Object = _model.flipArea;
	        var type:Array = ["side", "top", "bottom"];
	        for(var i:int = 0; i < type.length; i++){
	        	if (setting[type[i]]) {
	            	var fa:Sprite;
	            	if( page.face.getChildByName(type[i]) == null ){
		            	var flipArea:Sprite = new Sprite();
		            	flipArea.graphics.beginFill(0xff0000,0.01);
		            	flipArea.graphics.drawRect(0,0,30,30);
		            	
		            	flipArea.name = type[i];
		            	flipArea.useHandCursor = false;
		            	flipArea.tabEnabled = false;
		                fa = flipArea;
		                fa.addEventListener(MouseEvent.MOUSE_DOWN, faMouseDown);
		            	page.face.addChild(fa);
		         	}else{
		         		fa = page.face.getChildByName(type[i]) as Sprite;
		         	}
		         	
	                fa.x = _model.pageWidth * page.side;
	                fa.y = type[i] == "bottom" ? _model.pageHeight : 0;
	                fa.scaleX = (type[i] == "side" ? setting.sideWidth  : setting.cornerWidth) * (-page.side) * _model.pageWidth / 100		/100;
	                fa.scaleY = type[i] == "side" ? (_model.pageHeight / 30) : setting.cornerHeight * (type[i] == "top" ? 1 : -1) * _model.pageHeight / 100   /100   ;
	            } else if (page.face.getChildByName(type[i]) != null) {
	                delete page.face[type[i]];
		            page.face[type[i]] = null;
	            }
	        }
	    };
    
	    private function setPageFrame(page:Page):void {
	        var frame:uint = (page.index - _firstPage) + 1;
	        page.content.gotoAndStop(frame);	        
	    };
	    
	    private function setPageCount():void {
	        var cp:int = _curIndex;
	        var fc:int = _flipCount;
	        var count1:int = _model.visiblePages.left;
	        var count2:int = _model.visiblePages.right;
	        var index1:int = cp + (fc >= 0 ? 0 : fc * 2);
	        var index2:int = (cp + 1) + (fc <= 0 ? 0 : fc * 2);
	        for (var p:String in _pages) {
	            var page:Page = _pages[p];
	            var index:int = page.index;
	            if ( index < _firstPage || index > _lastPage || index < (index1 - count1 * 2) + 2 || index > index2 + count2 * 2 - 2 ) {
	                removePage(page);
	            }
	        }
	        var i:int = 0;
	        var index3:int;
	        while (i < count1) {
	            index3 = index1 - i * 2;
	            if (index3 < _firstPage) {
	                break;
	            }
	            if (_pages[index3] == null) {
	                createPage(index3, _model.topPageDepth + (index3 - _curIndex));
	            }
	            i++;
	        }
	        i = 0;
	        while (i < count2) {
	            index3 = index2 + i * 2;
	            if (index3 > _lastPage) {
	                return;
	            }
	            if (_pages[index3] == null) {
	                createPage(index3, _model.topPageDepth - (index3 - _curIndex));
	            }
	            i++;
	        }
	    };
	    
	    private function setEvenPage(page:Page):void {
	        if (page.side < 0) {
	            page.content.x = (_model.adjustEvenPage ? (-_model.pageWidth) : 0);
	        }
	    };
    
	    private function getMaskRange(x:Number, y:Number, cx:Number, cy:Number):Object {
	        var w:Number = _model.pageWidth;
	        var h:Number = _model.pageHeight;
	        var w_x:Number = (w - x) - cx;
	        var r:Object = {};
	        if (w_x <= 1) {
	            r.angle = 0;
	            r.type = 1;
	            r.a = 0;
	            r.b = 0;
	        } else {
	            var y_cy:Number = y - cy;
	            var h_y:Number = h - y;
	            r.angle = Math.atan2(y_cy, w_x);
	            var tan:Number = Math.tan(r.angle);
	            r.a = (w_x + (y_cy * y_cy / w_x)) / 2 + cx + cy * tan;
	            if (r.a <= h * tan) {
	                r.type = 0;
	                r.b = r.a / tan;
	            } else {
	                r.type = 1;
	                r.b = r.a - h * tan;
	            }
	        }
	        return r;
	    };
	    
	    private function drawRect(obj:Sprite, x:Number, y:Number, width:Number, height:Number, width2:Number=NaN):void {
	        obj.graphics.moveTo(x, y);
	        obj.graphics.lineTo(x + width, y);
	        obj.graphics.lineTo(x + (isNaN(width2) ? width : width2), y + height);
	        obj.graphics.lineTo(x, y + height);
	        obj.graphics.lineTo(x, y);
	    };
		
	    // TODO startFlip 开始翻页，同时创建新页
	    private function startFlip(index:int, cx:Number, cy:Number, aim_pos:Object):Page {
	        var cp:int = _curIndex;
	        var fc:int = _flipCount;// +右向左; -左向右
	        var page:Page;
	        // 翻动中的最左页 index
	        var index1:int = cp + (fc >= 0 ? 0 : fc * 2);
	        // 翻动中的最右页 index
	        var index2:int = cp + 1 + (fc <= 0 ? 0 : fc * 2);
	        if (index < index1 || index > index2) {
	            return undefined;
	        } else if (index == index1 || index == index2) {
	            if ( index == index1 && index <= (_firstPage & -2) ) 		{              return undefined;	            }
	            if ( index == index2 && index >= (_lastPage & -2 + 1)) 		{	           return undefined;	            }
	            
	            var n:int = fc * (index == index1 ? -1 : 1);
	            if (n >= _model.visiblePages.flip) {	           return undefined;	            }
	            if (n < 0) {
	                stopFlip(-1000, BooleanExt.TRUE, true);
	                fc = 0;
	                index1 = cp;
	                index2 = cp + 1;
	            }
	        } else {
	            page = _flipPages[fc > 0 ? ((index + 1) & -2) : ((index - 1) & -2 + 1)];
	            if (page == null) {          return undefined;        }
	            var next_page:Page = _flipPages[page.index - page.side * 2];
	            if (next_page != null) {     return undefined;        }
	        }
	        if (page == null) {
	            var top_depth:int = _model.topPageDepth;
	            var vp:int;
	            if (index == index1) {// 从右向左翻
	                vp = _model.visiblePages.left * 2;
	                fc = --_flipCount;
	                page = createPage(index - 1, top_depth - fc);
	                createPage(index - vp, top_depth + (index - vp - cp));
	            } else if (index == index2) {// 从左向右翻
	                vp = _model.visiblePages.right * 2;
	                fc = ++_flipCount;
	                page = createPage(index + 1, top_depth + fc);
	                createPage(index + vp, top_depth - (index + vp - cp));
	            }
	            _flipPages[page.index] = page;
	        }
	        // Re-set depth
	        settleDepth();
	        var pos:Object = page.position;
	        pos.cx = cx > 0 ? Math.min(int(_model.pageWidth * 0.9), cx) : 0;
	        pos.cy = cy > 0 ? Math.min(_model.pageHeight, cy) : 0;
	        pos.x = (_model.pageWidth - pos.cx) * (-page.side);
	        pos.y = pos.cy;
	        if (index > index1 && index < index2) {
	            var pt:Point = new Point(-pos.x, pos.y);//{x:-pos.x, y:pos.y};
	            page.face.localToGlobal(pt);
	            this.globalToLocal(pt);
	            if (Math.abs(pt.x) <= _model.pageWidth) {
	                pos.x = pt.x;
	                pos.y = pt.y;
	            }
	        }
	        if (aim_pos == null) {
	            page.state = "drag";
	            setAimPos(page.index, this.mouseX, this.mouseY);
	        } else {
	            page.state = "move";
	            setAimPos(page.index, aim_pos.x, aim_pos.y);
	        }
	        adjustPage(page);
	        return page;
	    };
	    
	    // TODO stopFlip 结束翻页，同时删除旧页
	    // (finish == false就动画结束，finish == true就瞬间结束)
	    // BooleanExt.NULL 自动判断翻过还是翻回，BooleanExt.TRUE总翻回, BooleanExt.FALSE总翻过
	    private function stopFlip(index:int, fold:String, finish:Boolean):void {
	        var fc:int = _flipCount;
	        var isFold:Boolean;
	        if (index == -1000 ) {// -1000代表Null
	            if (fc == 0) {
	                return;
	            }
	            var index1:int = (_curIndex + fc * 2) + (fc > 0 ? 0 : 1);
	            var index2:int = _curIndex + (fc > 0 ? 2 : -1);
	            isFold = (fold == BooleanExt.TRUE ? true : false);
	            if (!isFold) {
	                var t:int = index1;
	                index1 = index2;
	                index2 = t;
	            }
	            var i:int;
	            if (index2 > index1) {
	                i = index1;
	                while (i <= index2) {
	                    stopFlip(i, (isFold ? BooleanExt.TRUE: BooleanExt.FALSE), finish);
	                    i = i + 2;
	                }
	            } else {
	                i = index1;
	                while (i >= index2) {
	                    stopFlip(i, (isFold ? BooleanExt.TRUE: BooleanExt.FALSE), finish);
	                    i = i - 2;
	                }
	            }
	            return;
	        }
	        index = (fc > 0) ? ((index + 1) & -2) : (((index - 1) & -2) + 1);
	        var page:Page = _flipPages[index];
	        if (page == null) {
	            return;
	        }
	        if (fold == BooleanExt.NULL) {
	            isFold = (page.position.ax - page.position.x) * page.side < 0;
	        }else{
	        	isFold = (fold == BooleanExt.TRUE ? true : false);
	        }
	        var next_page:Page = _flipPages[index + (isFold ? -2 : 2) * page.side];
	        if ( next_page != null && (finish || next_page.state != (isFold ? "fold" : "unfold")) ) {
	            return;
	        }
	        if (finish) {
	            var side:int = page.side;
	            var vp:int;
	            if (isFold) {
	            	dispatchEvent( new FlipPageEvent(FlipPageEvent.ON_STOPFLIP, true, false, {page:page, fold:true, finish:true, curIndex:_curIndex}) );
	            	
	                removePage(page);
	                vp = _model.visiblePages[side > 0 ? "left" : "right"] * 2;
	                removePage(_pages[index - (vp - 1) * side]);
	                adjustPage(_pages[index + side]);
	            } else {
	                removePage(_pages[index + side]);
	                vp = _model.visiblePages[side < 0 ? "left" : "right"] * 2;
	                removePage(_pages[index + vp * side]);
	                _flipCount = _flipCount + side;
	                _curIndex = _curIndex - 2 * side;
	                page.state = "static";
	                page.position = {cx:0, cy:0};
	                delete _flipPages[index];
	                adjustPage(page);
	                var top_depth:int = _model.topPageDepth;
	                for (var j:String in _pages) {
	                    var pg:Page = _pages[j];
	                    var n:int = Math.abs( (pg.index - _curIndex) - (pg.side < 0 ? 0 : 1) );
	                    if (pg.state == "static") {
	                        pg.depth = (top_depth - n) - ((pg.side > 0) ? 1 : 0);
	                    } else {
	                        pg.depth = top_depth + (n / 2);
	                    }
	                }
	               dispatchEvent( new FlipPageEvent(FlipPageEvent.ON_STOPFLIP, true, false, {page:page, fold:false, finish:true, curIndex:_curIndex}) );
	            }
	        } else {
	            page.state = (isFold ? "fold" : "unfold");
	            var pos:Object = page.position;
	            pos.cx = 0;
	            pos.cy = pos.y >= pos.cy ? 0 : _model.pageHeight;
	            pos.ax = ((isFold ? -1 : 1) * page.side) * _model.pageWidth;
	            pos.ay = pos.cy;
	            /* pos.x = _pageWidth * page.side;
	            pos.y = pos.cy;
	            
	            var pot:Point = new Point(pos.x, pos.y);
	            page.face.localToGlobal(pot);
	            this.globalToLocal(pot);
	            
	            pos.x = pot.x;
	            pos.y = pot.y; */
	            adjustPage(page);
	            
	            var dx:Number = (pos.ax - pos.x);
	            var dy:Number = (pos.ay - pos.y);
	            if (Math.sqrt(dx * dx + dy * dy) <= _model.posPrecision) {
	                stopFlip(index, (page.state == "fold" ? BooleanExt.TRUE: BooleanExt.FALSE), true);
	            } else {
	            	//dispatchEvent( new FlipPageEvent(FlipPageEvent.ON_STOPFLIP, true, false, {page:page, fold:isFold, finish:false}) );
	            }
	        }
	    };
	    
	    private function setShadeStyle(page:Page):void {
	        var setting:Object = _model.shade;
	        var type:String = (page.side > 0 ? "right" : "left");
	        var m:Sprite = page.shade;
            if (m == null) {
                m = new SkinProxy.FlipPage_Shade() as Sprite;//new AssetManager.FlipPage_Shade() as Sprite;
                m.name = "ShadeMC";
                page.shade = m;
                page.shade.mouseEnabled = false;
                page.face.addChild(page.shade);
            }
            var pos:Object = page.position;
            var r:Number = pos.ratio;
            var style_min:Object = {alpha:setting[type + "AlphaMin"], size:setting[type + "SizeMin"]};
            var style_max:Object = {alpha:setting[type + "AlphaMax"], size:setting[type + "SizeMax"]};
            var style:Object = {alpha:(style_min.alpha * (1 - r)) + (style_max.alpha * r), size:(style_min.size * (1 - r)) + (style_max.size * r)};
            var w:Number = _model.pageWidth;
            var h:Number = _model.pageHeight;
            var ran:Object = pos.range;
            var top:int = pos.y >= pos.cy ? 1 : -1;
            m.alpha = style.alpha;
            m.x = (w - ran.a) * page.side;
            m.y = top > 0 ? 0 : h;
            m.scaleX = ((w * style.size) / 100) * page.side;
            m.scaleY = (ran.type == 0 ? Math.sqrt(ran.a * ran.a + ran.b * ran.b) : Math.sqrt((ran.a - ran.b) * (ran.a - ran.b) + h * h) + ran.b) * top;
            m.rotation = (((ran.angle * top) * (-page.side)) * 180) / Math.PI;
	    };
	    
	    private function setPageShadowStyle(page:Page):void {
	        var setting:Object = _model.pageShadow;
	        var m:Sprite = page.shadow;
	        if ((page.state != "static") && (setting.under || (setting.beside))) {
	            if (m == null) {
	                m = new Sprite();//new AssetManager.FlipPage_Shadow() as Sprite;
	                m.name = "ShadowMC";
	                page.shadow = m;
	                page.addChild(page.shadow);
	                page.swapChildren(page.shadow, page.page);
                
	                var shadowMaskMC:Sprite = new Sprite();
	                shadowMaskMC.name = "ShadowMaskMC";
	                page.shadowMask = shadowMaskMC;
	                page.addChild(page.shadowMask);
	                m.mask = page.shadowMask;
	                drawShadowMask(page);
	            }
	            var pos:Object = page.position;
	            var r2:Number = Math.pow(pos.ratio, 1);
	            var w:Number = _model.pageWidth;
	            var h:Number = _model.pageHeight;
	            var parts:Object = {under:1, beside:-1};
	            for (var i:String in parts) {
	                if (!setting[i]) {
	                    continue;
	                }
	                if( m.getChildByName(i) == null )
	                {
	                    var shadow:Sprite = new SkinProxy.FlipPage_Shadow() as Sprite;//new AssetManager.FlipPage_Shadow() as Sprite;
	                    shadow.name = i;
	                    shadow.mouseEnabled = false;
	                    m.addChild(shadow);
	                }
	                var alpha:Number = Math.round(setting[i + "AlphaMin"] * (1 - r2) + setting[i + "AlphaMax"] * r2);
	                var size:Number = setting[i + "SizeMin"] * (1 - r2) + setting[i + "SizeMax"] * r2;
	                m.getChildByName(i).alpha = alpha / 100;
	                m.getChildByName(i).scaleX = ((w * size / 100) * page.side * parts[i])/100;
	            }
	            var r:Object = pos.range;
	            var top:int = pos.y >= pos.cy ? 1 : -1;
	            m.alpha = ( r2 < 0.1 ? r2 * 1000 : (r2 < 0.9 ? 100 : (1 - r2) * 1000) )/100;
	            m.x = (w - r.a) * (-page.side);
	            m.y = (top > 0) ? 0 : h;
	            m.scaleY = ( r.type == 0 ? Math.sqrt(r.a * r.a + r.b * r.b) : Math.sqrt((r.a - r.b) * (r.a - r.b) + h * h) ) * top / 100;
	            m.rotation = (r.angle * top * page.side * 180) / Math.PI;
	        } else if (m != null) {
	        	if(page.contains(page.shadow)){
	        		page.removeChild(page.shadow);
	        	}
	        	if(page.contains(page.shadowMask)){
	        		page.removeChild(page.shadowMask);
	        	}
	            
	        }
	    };
	    
	    private function setAimPos(index:int, x:Number, y:Number):void {
	        var page:Page = _flipPages[index];
	        if (page == null || page.state == "static") {
	            return;
	        }
	        var pos:Object = page.position;
	        pos.ax = isNaN(x) ? 0 : Number(x);
	        pos.ay = isNaN(y) ? 0 : Number(y);
	        validatePos(page, true);
	    };
	    
	    // TODO settleDepth Reload page by depth
	    private function settleDepth():void{
	    	var pageArr:Array = BubbleSort2();
	    	for(var i:int = 0; i < pageArr.length; i++){
	    		removeChild(pageArr[i]);
	    	}
	    	for(var j:int = 0; j < pageArr.length; j++){
	    		addChild(pageArr[j]);
	    	}
	    	
	    }
	    
		// TODO Bubble Sort ( from small to big ) Reset the depth for pages
		private function BubbleSort2():Array{
			
			var SeqList:Array = new Array();
			for (var m:String in _pages){
				SeqList.push(_pages[m]);
			}
			
			var i:uint;
			var j:uint;
			var n:uint = SeqList.length;
			
			for (i = 0; i < n-1; i++){ // 最多做n-1趟排序

				for (j = 1; j < n-i; j++){ // 对当前无序区R[i..n]自下向上扫描
				
					var value1:Number = Page(SeqList[j]).depth;
					var value2:Number = Page(SeqList[j-1]).depth;
					
					if (value1 < value2){// 交换记录
						
						var temp:Page;
						temp = SeqList[j-1]; // R[0]不是哨兵，仅做暂存单元
						SeqList[j-1] = SeqList[j];
						SeqList[j] = temp;

					}
				}
			}
			
			return SeqList;				
		}
	    
	    // Detect if the page should be autoFlip after "drag" or "move"
	    private function detectFlip(evt:TimerEvent):void{
	    	var page:Page = _pages[_dragingIndex];
	    	if ((page.state == "move") && ((page.position.x * (-page.side)) > 0)) {
                return;
            }
            stopFlip(page.index, BooleanExt.FALSE, false);
            _autoFlip.stop();
	    }
	    
	    private function gotoPage(index:int, rebuild:Boolean):void {
	        index = index > _lastPage ? _lastPage : (index > _firstPage ? index : _firstPage);
	        index = index & -2;	// 把 index 变成最大的小于它的偶数, 如 15变成14, 14变成14
	        var diff:int = index - _curIndex;
	        if (diff == 0) {       return ;	        }
	        var pages:Object = {};
	        var flip_pages:Object = {};
	        for (var i:String in _pages) {
	            var page:Page = _pages[i];
	            var new_index:int = page.index + diff;
	            page.name = "Page" + new_index;
	            page.index = new_index;
	            pages[new_index] = page;
	            if (page.state != "static") {
	                flip_pages[new_index] = page;
	            }
	        }
	        _pages = pages;
	        _flipPages = flip_pages;
	        _curIndex = index;
	        setPageCount();
	        if (rebuild) {
	            //createPageContent(_pages[index]);
	        }
	    };
	    
	    private function autoFlip(params:Object):void {
	    	var index:int = params.index;
	    	var angle:Number = params.angle;
	        var cp:int = _curIndex + _flipCount * 2;
	        if (index > cp || index < cp) {
	        	flipToPage(index, angle, -1);
	        }else{
	        	_autoFlip.stop();
	        }
	    };
	    
	    private function clearPage():void{
	    	for (var i:String in _pages) {
	            var page:Page = _pages[i];
	            removeChild(page);
	            page.dispose();
	        }
	        _pages = {};
	    }
    
		// Events
		private function faMouseDown(evt:MouseEvent):void {
			var page:Page = Page(evt.target.parent.parent.parent);
			var fa:Sprite = Sprite(evt.target);
			//fa.removeEventListener(MouseEvent.MOUSE_DOWN, faMouseDown);
			var index:int = page.index;
			_dragingIndex = index;
			var faObj:Object = {top:(_model.flipArea.flipMarginY * _model.pageHeight) / 100, bottom:((100 - _model.flipArea.flipMarginY) * _model.pageHeight) / 100, side:page.content.mouseY};
			var faName:String = fa.name;
			startFlip(	_dragingIndex, 
						(_model.flipArea.flipMarginX * _model.pageWidth) / 100,
						faObj[faName], null);
		};
		private function onStageMouseUp(evt:MouseEvent):void {
		    stopFlip(_dragingIndex, BooleanExt.NULL, false);
		};
		
		// Update destination position for "position" property in "_flipPages"
		private function onMouseMove(evt:MouseEvent):void {
		    //var mx:Number = evt.stageX - parent.parent.x - parent.x;
		    //var my:Number = evt.stageY - parent.parent.y - parent.y;
		    var localPot:Point = globalToLocal(new Point(evt.stageX, evt.stageY));
		    var mx:Number = localPot.x;
		    var my:Number = localPot.y;
		    
		    var i:String;
		    for (i in _flipPages) {
		        var state:Object = Page(_pages[i]).state;
		        if (state == "drag") {
                	setAimPos(Page(_pages[i]).index, mx, my);
                }
		    }
		};
		
		// TODO Update index Range after addPage be called
		private function onAddpage(evt:FlipPageEvent):void{
			var addOn:int = _model.hasCover? 1 : 0;
			setIndexRange(0 + addOn, (_model.pageList.length - 1) + addOn);
		}
		
		
		// TODO Main cycle for FlipPage engine
		private function doFlipping(objEvent:Event):void {
	        for (var i:String in _flipPages) {
	            var page:Page = _flipPages[i];
	            var pos:Object = page.position;
	            var acc:Number;
	            switch(page.state){
	            	case "drag":
	            		acc = _model.accRatio.drag;
	            		break;
            		case "move":
            			acc = _model.accRatio.move;
            			break;
            		case "fold":
            			acc = _model.accRatio.fold;
            			break;
            		case "unfold":
            			acc = _model.accRatio.unfold;
            			break;
	            }
	            pos.x = pos.x + (pos.ax - pos.x) * acc;
	            pos.y = pos.y + (pos.ay - pos.y) * acc;
	            adjustPage(page);
	            if (page.state == "fold" || page.state == "unfold") {
	                var dx:Number = pos.ax - pos.x;
	                var dy:Number = pos.ay - pos.y;
	                if ( (dx * dx + dy * dy) <= _model.posPrecision * _model.posPrecision ) {
	                    stopFlip(page.index, (page.state == "fold" ? BooleanExt.TRUE: BooleanExt.FALSE), true);
	                }
	            }
	        }
	    };
	    
	    public function Dispose():void{
	    	clearPage();
	    	
			_stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			_stage.removeEventListener(MouseEvent.MOUSE_UP,onStageMouseUp);
	    	_model.removeEventListener(FlipPageEvent.ON_ADDPAGE, onAddpage);
	    	_autoFlip.removeEventListener( TimerEvent.TIMER, detectFlip );
	    	_mainTimer.removeEventListener( TimerEvent.TIMER,doFlipping );
	    	//fa.removeEventListener(MouseEvent.MOUSE_DOWN, faMouseDown);
	    	removeAllChilds();
	    }
	    
	    // Get and Set
	    public function get curIndex():int						{		return _curIndex;	}
	}
}